/* eslint-disable react-hooks/rules-of-hooks */
import { useDebounceFn } from "ahooks";
import { useEffect, useRef, useState } from "react";
import { IDataItem, IHeader, IOffset } from "./types";
import { useUpdateEffect } from "./use";

const overscan = 10;

export const useVirtualScorll = () => app => {
  const { run } = useDebounceFn(fn => fn(), { wait: 500 });

  let list: IDataItem[] = app.ctx.dataSource;

  const [offset, setOffset]: [IOffset, any] = useState({ x: 0, y: 0 });

  const [result, setResult] = useState({
    scrollStatus: -1,
    viewData: [],
    header: [],
    offset: { offsetX: 0, offsetY: 0 }
  });

  const containerRef = useRef(null);

  app.ctx.containerRef = containerRef;

  const calculateRange = () => {
    let { itemHeight, header } = app.ctx.viewInfo;

    const element = containerRef.current;

    if (element) {
      const logicOffset = getOffset(
        header,
        itemHeight,
        element.scrollLeft,
        element.scrollTop
      );

      const viewCapacity = getViewCapacity(
        itemHeight,
        logicOffset,
        app.ctx.viewInfo,
        { x: header.length, y: list.length }
      );

      let scrollStatus = 0;
      let fromY = logicOffset.y - overscan;
      if (fromY < 0) (fromY = 0), (scrollStatus = -1);

      let toY = logicOffset.y + viewCapacity.y + overscan;
      if (toY > list.length) (toY = list.length), (scrollStatus = 1);

      return {
        scrollStatus,
        viewCapacity,
        overscan,
        offset: logicOffset,
        from: {
          x: logicOffset.x,
          y: fromY
        },
        to: {
          x: logicOffset.x + overscan,
          y: toY
        }
      };
    } else {
      // return {
      //   from: { x: offset.x, y: 0 },
      //   to: {
      //     x: offset.x + overscan,
      //     y: 0
      //   }
      // };
    }
  };

  const getViewData = function() {
    let result = calculateRange();

    let viewInfo = app.ctx.viewInfo;

    let { itemWidth, itemHeight, header } = viewInfo;

    // let x = ~~(offset.x / itemWidth);
    // let y = ~~(offset.y / itemHeight);

    let x = result.offset.x;
    let y = result.offset.y;

    let offsetY = -offset.y % itemHeight;
    let offsetX = -offset.x % itemWidth;

    let viewData: IDataItem[][] = [];
    let viewHeader: IHeader[] = [];

    if (header) {
      app.ctx.viewHeader = viewHeader = getRowData(
        header,
        header,
        viewInfo.fixed,
        viewInfo.viewWidth * viewInfo.preRenderRatio,
        x
      );
    }

    let renderWidth = viewHeader.reduce((pre, cur) => cur.width + pre, 0);

    // let viewHeight = viewInfo.viewHeight * viewInfo.preRenderRatio;

    // let height = 0;

    // let vY = 0;
    // for (; height < viewHeight; height += itemHeight, vY++) {
    //   if (!list[y + vY]) {
    //     // viewData[vY]=[]
    //     console.log(
    //       "%c 🥒 //: break",
    //       "font-size:20px;background-color: #ED9EC7;color:#fff;"
    //     );
    //     break;
    //   }
    //   if (vY < viewInfo.fixed) {
    //     // 为固定行预留
    //     viewData[vY] = getRowData(
    //       header,
    //       list[y + vY],
    //       viewInfo.fixed,
    //       viewInfo.viewWidth * viewInfo.preRenderRatio,
    //       x
    //     );
    //   } else {
    //     viewData[vY] = getRowData(
    //       header,
    //       list[y + vY],
    //       viewInfo.fixed,
    //       viewInfo.viewWidth * viewInfo.preRenderRatio,
    //       x
    //     );
    //   }
    // }

    console.log(
      "%c 🍶 from to: ",
      "font-size:20px;background-color: #FFDD4D;color:#fff;",
      //   y,
      //   y + vY - 1,
      result.offset.y,
      " | ",
      result.from.y,
      result.to.y,
      result
    );

    let i = 0;
    for (let vY = result.from.y; vY < result.to.y; vY++, i++) {
      if (!list[vY]) {
        break;
      }
      viewData[i] = getRowData(
        header,
        list[vY],
        viewInfo.fixed,
        viewInfo.viewWidth * viewInfo.preRenderRatio,
        x
      );
    }

    console.log(
      "%c 🥤 viewData: ",
      "font-size:20px;background-color: #2EAFB0;color:#fff;",
      viewData,
      viewHeader
    );

    console.log("renderWidth", viewHeader, renderWidth, viewData.length);
    setResult({
      scrollStatus: result.scrollStatus,
      viewData,
      header: viewHeader,
      offset: { offsetX, offsetY: result.scrollStatus === 0 ? offsetY : 0 }
    });
  };

  app.actions.getViewData = getViewData;

  // useEffect(() => {
  //   app.actions.clearScroll = () => containerRef.current?.scrollTo(0, 0);
  // }, [setOffset]);

  useEffect(() => {
    app.ctx.wrapperPropsStyle = {
      width: add(app.ctx?.viewInfo?.header, "width") || 500,
      height: (app.ctx?.viewInfo?.itemHeight || 50) * app.ctx?.viewInfo?.y
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [app.ctx?.viewInfo?.header, app.ctx?.viewInfo?.itemHight]);

  useUpdateEffect(() => {
    getViewData();
  }, [offset, list, setResult]);

  // /** 半格自动变成整格 */
  // useEffect(() => {
  //   const automaticAlignment = () => {
  //     console.log(
  //       "%c 🍫 result.offset: ",
  //       "font-size:20px;background-color: #93C0A4;color:#fff;",
  //       app.ctx.shouldAutomaticAlignment,
  //       [result.offset.offsetX, result.offset.offsetY]
  //     );
  //     if (
  //       !app.ctx.mouseDown &&
  //       [result.offset.offsetX, result.offset.offsetY].some(i => i !== 0)
  //     ) {
  //       containerRef.current.scrollTop =
  //         containerRef.current.scrollTop + result.offset.offsetY;
  //       containerRef.current.scrollLeft =
  //         containerRef.current.scrollLeft + result.offset.offsetX;
  //     }
  //   };

  //   run(automaticAlignment);

  //   console.log(
  //     "%c 🍫 result.offset: ",
  //     "font-size:20px;background-color: #93C0A4;color:#fff;",
  //     result.offset
  //   );
  // }, [result.offset, setOffset, run, app]);

  return {
    props: ctx => {
      let { itemHeight, viewWidth, viewHeight } = app.ctx.viewInfo || {};
      console.log(
        "%c 🥘 app.ctx: ",
        "font-size:20px;background-color: #E41A6A;color:#fff;",
        app.ctx
      );

      return {
        containerProps: {
          ref: (ele: any) => {
            containerRef.current = ele;
          },
          onMouseDown: () => {
            app.ctx.mouseDown = true;
          },
          onMouseUp: () => {
            app.ctx.mouseDown = false;
          },
          onScroll: (e: any) => {
            //    return
            let finalX = e.target.scrollWidth - viewWidth;
            let finalY = e.target.scrollHeight - viewHeight;
            let offset = {
              x: e.target.scrollLeft > finalX ? finalX : e.target.scrollLeft,
              y: e.target.scrollTop > finalY ? finalY : e.target.scrollTop
            };

            // offset = e.target?.getBoundingClientRect?.();
            console.log(
              "%c 🥩 offset: ",
              "font-size:20px;background-color: #ED9EC7;color:#fff;",
              offset
            );
            //  {
            //     x: 375,
            //     y: 339.796875,
            //     width: 422,
            //     height: 300,
            //     top: 339.796875,
            //     right: 797,
            //     bottom: 639.796875,
            //     left: 375
            //   };

            // console.log(
            //   "debugger-1   e.target.scrollWidth ,finalX, offset.x",
            //   e.target.scrollWidth,
            //   finalX,
            //   offset.x,
            //   finalY,
            //   offset.y
            // );
            // if (e.target.scrollWidth > 80000) {
            //   debugger;
            // }

            setOffset(offset);
          },
          style: {
            position: "relative",
            overflow: "auto",
            width: viewWidth,
            height: viewHeight
          }
        },
        wrapperProps: {
          style: app.ctx.wrapperPropsStyle || {}
        },
        tableProps: {
          position: "absolute",
          top: result.scrollStatus === -1 ? 0 : offset.y - 11 * 30,
          left: offset.x
        },
        offset: {
          "--tableOffsetX": result.offset.offsetX + "px",
          "--tableOffsetY": result.offset.offsetY + "px"
        },
        data: {
          ...result,
          viewInfo: { itemHeight }
        }
      };
    }
  };
};

const add = (arr: any[], key: string) => {
  return arr?.reduce?.((acc: number, cur: any) => acc + cur[key], 0);
};

/**
 * @description 获取一行数据
 * @author hcluo
 * @date 06/12/2020
 * @param {*} fixedNum
 * @param {*} header
 * @param {*} fullRow
 * @param {*} viewWidth
 * @param {number} x
 * @return {*}
 */

function getRowData(
  header: any,
  fullRow: any,
  fixedNum: any,
  viewWidth: any,
  x: number
) {
  let width = 0,
    colNum = 0;
  let rowData: any = [];

  for (; colNum < fixedNum; width += header[colNum].width, colNum++)
    rowData[colNum] = fullRow[colNum];

  for (; width < viewWidth; width += header[x + colNum].width, colNum++) {
    if (!header[x + colNum]) {
      //   rowData[colNum] = {};
      break;
    }
    rowData[colNum] = fullRow[x + colNum];
  }

  return rowData;
}

/**
 * @description  获取表格可视区域数据位移
 * @author hcluo
 * @date 17/12/2020
 * @param {*} header
 * @param {number} itemHeight
 * @param {number} scrollLeft
 * @param {number} scrollTop
 * @return {*}
 */
const getOffset = (
  header: any,
  itemHeight: number,
  scrollLeft: number,
  scrollTop: number
): IOffset => {
  let sumX = 0;
  let x = 0;
  for (let i = 0; i < header.length; i++) {
    sumX += header[i].width;
    if (sumX >= scrollLeft) {
      x = i + 1;
      break;
    }
  }

  let y = Math.floor(scrollTop / itemHeight) + 1;

  return { x, y };
};

/**
 * @description  获取可视区域数据横纵向位移量
 * @author hcluo
 * @date 29/12/2020
 * @param {*} itemHeight
 * @param {IOffset} offset
 * @param {*} viewInfo
 * @param {*} dataInfo
 * @return {*}
 */
const getViewCapacity = (itemHeight, offset: IOffset, viewInfo, dataInfo) => {
  let x = offset.x,
    y;

  if (typeof itemHeight === "number") {
    y = Math.ceil(viewInfo.viewHeight / itemHeight);
  } else {
    const start = offset.y;
    let sum = 0;
    let capacity = 0;
    for (let i = start; i < dataInfo.y; i++) {
      const height = (itemHeight as (index: number) => number)(i);
      sum += height;
      if (sum >= viewInfo.viewHeight) {
        capacity = i;
        break;
      }
    }
    y = capacity - start;
  }
  return { x: x, y };
};
